package juego;

import java.util.ArrayList;
import java.util.Iterator;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;



public class Tripulado extends Movil {

	
	//atributos de clase
	private static int miOrigenEnX = 170;
	private static int miOrigenEnY = 350;
	private static int miResistenciaMaxima = 30;
	private static int miTamanio = 20;//30;//5;
	private static int miVelocidad = 10;//15;
	private static int miImpacto = 10;
	private static int miCapacidadArmamento = 3;
	private static int minLimiteEnX = 0;
	private static int minLimiteEnY = 24;
	private static int maxLimiteEnX = 350;
	private static int maxLimiteEnY = 430;
	
	
	//atributos de instancia
	private Energia resistencia;
	private Disparable armamento[]; 
	private int cantidadArmas;
	//metodos
	
	//constructor. siempre tiene la misma ubicacion inicial
	public Tripulado() {
		super (miOrigenEnX, miOrigenEnY);
		this.resistencia = new Energia (miResistenciaMaxima);
		this.armamento = new Disparable[miCapacidadArmamento];
		this.armamento[0] = new ArmaLaser();
		this.cantidadArmas = 1;
	};
	
	//Devuelve el impacto provocado
    @Override
	public Impacto impactoProvocado(){
		return new ImpactoLineal(miImpacto);
	};   

	@Override
	public boolean esNaveEnemiga() {
		return false;
	};

	@Override
	public boolean esProyectil() {
		return false;
	};

	@Override
	public boolean verLealtad(){
		return true;
	};
	
	@Override
	public int verTamanio(){
		return miTamanio;
	};

	public void recargarEnergia(int mejora) {
		this.resistencia.incrementarEnergia(mejora);
	};
	
	//crea una nueva ArmaCohete y ArmaTorpedoRastreador
	//y los posiciona donde estaban los mismos previamente. 
	public void recargarArmamento(){
		this.armamento[1] = new ArmaCohete();
		this.armamento[2] = new ArmaTorpedoRastreador();
		this.cantidadArmas = 3;
	}
	
	public int verResistencia(){
		return this.resistencia.verEnergia();
	};
	
	//pre: recibe como parametros las listas de moviles y de botines.
	//post: actualiza las listas y retorna los puntos que deban computarse.
	//si no esta vivo lanza una excepcionMuerto
	@Override
	public int vivir(ArrayList<Movil> listaDeMoviles, ArrayList<Botin> listaDeBotines){
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		//impacto
		int puntaje = this.impactar(listaDeMoviles,listaDeBotines);
		this.recuperarBotin(listaDeBotines);
		return puntaje;
	};
	
	//resuelve el choque. retorna el puntaje
	protected int impactoPropio(Movil movil, ArrayList<Movil> listaDeMoviles, ArrayList<Botin> listaDeBotines){
		int puntaje = 0;
		//solo puede impactar moviles enemigos
		if (!movil.verLealtad()){
			//movil hiere a this
			movil.impactoProvocado().herir(this.resistencia);
			//movil recibe el impacto. se elimina de la lista si corresponde.se modifica el puntaje
			puntaje += movil.recibirImpacto(this.impactoProvocado(), listaDeMoviles, listaDeBotines);
		}
		return puntaje;
	}
	
	//se encarga de destruirse si corresponde. devuelve el puntaje
	protected int verificarDestruccion(ArrayList<Movil> listaDeMoviles, ArrayList<Botin> listaDeBotines){
		//si queda energia no se destruye y retorna 0
		if(this.resistencia.quedaEnergia()){
			return 0;
		}
		//si se destruye retorna 0
		this.destruirse(listaDeMoviles);
		return 0;
	}
	
	
	//se encarga de recorrer la lista de botines y utilizar los que encuentre
	private void recuperarBotin(ArrayList<Botin> listaDeBotines) {
		Iterator<Botin> iterador = listaDeBotines.iterator();
		while (iterador.hasNext()){
			Botin botin = iterador.next();
			try{
				if (botin.huboContacto(this)){
					botin.mejorar(this);
					iterador.remove();
				}
			}
			catch (ExcepcionMismoElemento e){};
		}
	};
				
				

	

	

	
	@Override
	//se elimina de la lista si corresponde. devuelve 0
	public int recibirImpacto(Impacto impacto, ArrayList<Movil> listaDeMoviles, ArrayList<Botin> listaDeBotines){
		impacto.herir(this.resistencia);
		int puntaje = this.verificarDestruccion(listaDeMoviles, listaDeBotines);
		return puntaje;
	}
	
	
	//si esta muerto lanzan una excepcionMuerto
	
	public void moverEnXPositivo(){
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		int enX = getX() + miVelocidad;
		int enY = getY();
		Coordenada ubicacion = new Coordenada(enX,enY);
		if (!this.EstaFueraDelEscenario(ubicacion)) {
			 this.ubicacion.moverEnX(miVelocidad);
		}

	}
	
	public void moverEnXNegativo(){
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		int enX = getX() - miVelocidad;
		int enY = getY();
		Coordenada ubicacion = new Coordenada(enX,enY);
		if (!this.EstaFueraDelEscenario(ubicacion)) {
			this.ubicacion.moverEnX(-miVelocidad);
		}
       
	}

	public void moverEnYNegativo(){
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		int enX = getX();
		int enY = getY() - miVelocidad;
		Coordenada ubicacion = new Coordenada(enX,enY);
		if (!this.EstaFueraDelEscenario(ubicacion)) {
            this.ubicacion.moverEnY(-miVelocidad);
		}
	}
	
	public void moverEnYPositivo(){
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		int enX = getX();
		int enY = getY() + miVelocidad;
		Coordenada ubicacion = new Coordenada(enX,enY);
		if (!this.EstaFueraDelEscenario(ubicacion)) {
            this.ubicacion.moverEnY(miVelocidad);
		}
	}

	//devuelve una lista de Moviles con todos los proyectiles disparados
	//lanza ExcepcionMuerto
	public ArrayList<Movil> disparar() {
		if (!this.vivo){
			throw new ExcepcionMuerto();
		}
		ArrayList<Movil> proyectiles = new ArrayList<Movil>();
		int lugarDisparo = 0;
		Coordenada lugar;
		int lugarX = this.ubicacion.obtenerCoordenadaX();
		int lugarY = this.ubicacion.obtenerCoordenadaY();
		int indice = 0;
		while (indice < this.cantidadArmas){
			try{
				lugar = new Coordenada(lugarX + lugarDisparo, lugarY);
				Proyectil proyectil = this.armamento[indice].disparar(lugar);
				proyectil.hacerLeal();
				
				proyectiles.add(proyectil);
				lugarDisparo++;
			}
			catch(ExcepcionMunicionAgotada e){}
			indice++;
		}
		return proyectiles;
	};

	
	private boolean EstaFueraDelEscenario(Coordenada ubicacion){
		if (ubicacion.obtenerCoordenadaX() < minLimiteEnX ||
			ubicacion.obtenerCoordenadaX() > maxLimiteEnX ||
			ubicacion.obtenerCoordenadaY() < minLimiteEnY ||
			ubicacion.obtenerCoordenadaY() > maxLimiteEnY){
				return true;}
		return false;
	}
	
	private void persistirArmamento(Document doc, Element elemento) {
		Element armamento = doc.createElement("Armamento");
		for (int i = 0; i < this.cantidadArmas; i++ ) {
			Disparable arma = this.armamento[i];
			armamento.appendChild(arma.getElement(doc));
		}
		elemento.appendChild(armamento);
	}
	

	public Element getElement(Document doc) {
		
		Element nave = doc.createElement("Tripulado");
		
		//de Tripulado
		Integer valorCantidadArmas = new Integer(this.cantidadArmas);
		Element cantidadArmas = doc.createElement("CantidadArmas");
		nave.appendChild(cantidadArmas);
		cantidadArmas.setTextContent(valorCantidadArmas.toString());
		
		Element resistencia = this.resistencia.getElement(doc);
		nave.appendChild(resistencia);
		
		this.persistirArmamento(doc, nave);
				
		//de Movil
		this.persistirMovil(doc, nave);
		
		return nave;
	}
	
	 public static Tripulado fromElement(Element element) {
		  Tripulado nave = new Tripulado();
		  Movil.recuperarMovil(element,nave);
		  recuperarPropios(element,nave);
		  return nave;
	}
	    
	    private static void recuperarPropios(Element element, Tripulado tripulado) {
			NodeList childs = element.getChildNodes();
			for (int i = 0; i < childs.getLength(); i++) {
				Node child = childs.item(i);
				if (child.getNodeName().equals("CantidadArmas")) {
					tripulado.cantidadArmas = Integer.parseInt(child.getTextContent());
				}
				else if (child.getNodeName().equals("Energia")) {
					tripulado.resistencia = Energia.fromElement((Element) child);
				}
				else if (child.getNodeName().equals("Armamento")) { 
					//obtengo a su vez la lista de hijos de este nodo
					NodeList listaNietos = child.getChildNodes();
					for (int j = 0; j < listaNietos.getLength();j++) {
						Node nieto = listaNietos.item(j);
						Disparable arma = recuperarArmamento(nieto);
						tripulado.armamento[j] = arma;
					}
				}
			}
		}
	    
	    
	    
	    private static Disparable recuperarArmamento(Node nieto) {
			Disparable arma = null;
			String nombreNieto = nieto.getNodeName();
			
			if (nombreNieto.equals("ArmaLaser") ){
				arma = ArmaLaser.fromElement((Element)nieto);
			}
			else if (nombreNieto.equals("ArmaCohete") ){
				arma = ArmaCohete.fromElement((Element)nieto);
			}
			else if (nombreNieto.equals("ArmaTorpedoSimple") ){
				arma = ArmaTorpedoSimple.fromElement((Element)nieto);
			}
			else if (nombreNieto.equals("ArmaTorpedoRastreador") ){
				arma = ArmaTorpedoRastreador.fromElement((Element)nieto);
			}
			else if (nombreNieto.equals("ArmaTorpedoAdaptable") ){
				arma = ArmaTorpedoAdaptable.fromElement((Element)nieto);
			}
			
			return arma;
		}
	
	
	


}
